1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.testing;
18  
19  import com.google.common.annotations.GwtCompatible;
20  
21  import java.util.ArrayList;
22  import java.util.Collection;
23  import java.util.Collections;
24  
25  /**
26   * An {@link ClusterException} is a data structure that allows for some code to
27   * "throw multiple exceptions", or something close to it. The prototypical code
28   * that calls for this class is presented below:
29   *
30   * <pre>
31   * void runManyThings(List&lt;ThingToRun&gt; thingsToRun) {
32   *   for (ThingToRun thingToRun : thingsToRun) {
33   *     thingToRun.run(); // <-- say this may throw an exception, but you want to
34   *                       // always run all thingsToRun
35   *   }
36   * }
37   * </pre>
38   *
39   * <p>This is what the code would become:
40   *
41   * <pre>
42   * void runManyThings(List&lt;ThingToRun&gt; thingsToRun) {
43   *   List&lt;Exception&gt; exceptions = Lists.newArrayList();
44   *   for (ThingToRun thingToRun : thingsToRun) {
45   *     try {
46   *       thingToRun.run();
47   *     } catch (Exception e) {
48   *       exceptions.add(e);
49   *     }
50   *   }
51   *   if (exceptions.size() > 0) {
52   *     throw ClusterException.create(exceptions);
53   *   }
54   * }
55   * </pre>
56   *
57   * <p>See semantic details at {@link #create(Collection)}.
58   *
59   * @author Luiz-Otavio Zorzella
60   */
61  @GwtCompatible
62  final class ClusterException extends RuntimeException {
63  
64    public final Collection<? extends Throwable> exceptions;
65  
66    private ClusterException(Collection<? extends Throwable> exceptions) {
67      super(
68          exceptions.size() + " exceptions were thrown. The first exception is listed as a cause.",
69          exceptions.iterator().next());
70      ArrayList<Throwable> temp = new ArrayList<Throwable>();
71      temp.addAll(exceptions);
72      this.exceptions = Collections.unmodifiableCollection(temp);
73    }
74  
75    /**
76     * @see #create(Collection)
77     */
78    public static RuntimeException create(Throwable... exceptions) {
79      ArrayList<Throwable> temp = new ArrayList<Throwable>();
80      for (Throwable exception : exceptions) {
81        temp.add(exception);
82      }
83      return create(temp);
84    }
85  
86    /**
87     * Given a collection of exceptions, returns a {@link RuntimeException}, with
88     * the following rules:
89     *
90     * <ul>
91     *  <li>If {@code exceptions} has a single exception and that exception is a
92     *    {@link RuntimeException}, return it
93     *  <li>If {@code exceptions} has a single exceptions and that exceptions is
94     *    <em>not</em> a {@link RuntimeException}, return a simple
95     *    {@code RuntimeException} that wraps it
96     *  <li>Otherwise, return an instance of {@link ClusterException} that wraps
97     *    the first exception in the {@code exceptions} collection.
98     * </ul>
99     *
100    * <p>Though this method takes any {@link Collection}, it often makes most
101    * sense to pass a {@link java.util.List} or some other collection that
102    * preserves the order in which the exceptions got added.
103    *
104    * @throws NullPointerException if {@code exceptions} is null
105    * @throws IllegalArgumentException if {@code exceptions} is empty
106    */
107   public static RuntimeException create(Collection<? extends Throwable> exceptions) {
108     if (exceptions.size() == 0) {
109       throw new IllegalArgumentException(
110           "Can't create an ExceptionCollection with no exceptions");
111     }
112     if (exceptions.size() == 1) {
113       Throwable temp = exceptions.iterator().next();
114       if (temp instanceof RuntimeException) {
115         return (RuntimeException)temp;
116       } else {
117         return new RuntimeException(temp);
118       }
119     }
120     return new ClusterException(exceptions);
121   }
122 }